package com.chad.library.adapter.base

import android.annotation.SuppressLint
import android.util.SparseIntArray
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.DiffUtil
import com.chad.library.adapter.base.diff.BrvahListUpdateCallback
import com.chad.library.adapter.base.entity.BaseMultiPartItemEntity
import com.chad.library.adapter.base.viewholder.BaseViewHolder

/**
 * 一个Item由多个部分组成基础适配器
 *
 * @author Created by wanggaowan on 2022/9/7 15:58
 */
@SuppressLint("NotifyDataSetChanged")
abstract class BaseMultiPartItemQuickAdapter<T : BaseMultiPartItemEntity, VH : BaseViewHolder>
@JvmOverloads constructor(data: List<T>? = null) : RvQuickAdapter<T, VH>(0, data) {

    private val layouts: SparseIntArray by lazy(LazyThreadSafetyMode.NONE) { SparseIntArray() }

    /**
     * 数据总量
     */
    private var totalCount = 0

    init {
        notifyDataChange()
    }

    override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): VH {
        val layoutResId = layouts.get(viewType)
        require(layoutResId != 0) { "ViewType: $viewType found layoutResId，please use addItemType() first!" }
        return createBaseViewHolder(parent, layoutResId)
    }

    /**
     * 调用此方法，设置多布局
     * @param type Int
     * @param layoutResId Int
     */
    protected fun addItemType(type: Int, @LayoutRes layoutResId: Int) {
        layouts.put(type, layoutResId)
    }

    /**
     * 转化数据，计算每个数据的起始位置并返回新增数据的长度
     */
    @JvmOverloads
    protected fun mapData(list: List<T>?, start: Int = 0): Int {
        var size = start
        list?.forEach {
            it.startIndex = size
            size += it.getTotalCount()
        }
        return size - start
    }

    override fun setList(list: List<T>?) {
        totalCount = mapData(list)
        super.setList(list)
    }

    override fun addData(data: T) {
        data.startIndex = totalCount
        val addCount = data.getTotalCount()
        totalCount += addCount
        this.data.add(data)
        if (addCount == 1) {
            notifyItemInserted(data.startIndex + headerLayoutCount)
        } else {
            notifyItemRangeInserted(data.startIndex + headerLayoutCount, addCount)
        }
        compatibilityDataSizeChanged(addCount)
    }

    override fun addData(newData: List<T>) {
        if (newData.isEmpty()) {
            return
        }

        val preCount = totalCount
        totalCount += mapData(newData, totalCount)
        val addCount = totalCount - preCount
        this.data.addAll(newData)
        notifyItemRangeInserted(preCount + headerLayoutCount, addCount)
        compatibilityDataSizeChanged(addCount)
    }

    /**
     * 将数据加入到列表指定位置，[position]为[data]中数据下标
     */
    override fun addData(position: Int, data: T) {
        this.data.add(position, data)
        notifyDataChange()
        val addCount = data.getTotalCount()
        if (addCount == 1) {
            notifyItemInserted(data.startIndex + headerLayoutCount)
        } else {
            notifyItemRangeInserted(data.startIndex + headerLayoutCount, addCount)
        }
        compatibilityDataSizeChanged(addCount)
    }

    /**
     * 将数据加入到列表指定位置，[position]为[data]中数据下标
     */
    override fun addData(position: Int, newData: List<T>) {
        if (newData.isEmpty()) {
            return
        }

        this.data.addAll(position, newData)
        notifyDataChange()
        val startIndex = newData[0].startIndex
        val lastData = newData[newData.size - 1]
        val addCount = lastData.startIndex - startIndex + lastData.getTotalCount()
        if (addCount == 1) {
            notifyItemInserted(startIndex + headerLayoutCount)
        } else {
            notifyItemRangeInserted(startIndex + headerLayoutCount, addCount)
        }
        compatibilityDataSizeChanged(addCount)
    }

    /**
     * 将指定下标位置数据移除，[position]为[data]中数据下标
     */
    override fun removeAt(position: Int) {
        if (position >= data.size) {
            return
        }

        val data = this.data.removeAt(position)
        notifyDataChange()
        val removeCount = data.getTotalCount()
        if (removeCount == 1) {
            notifyItemRemoved(data.startIndex + headerLayoutCount)
        } else {
            notifyItemRangeRemoved(data.startIndex + headerLayoutCount, removeCount)
        }
        compatibilityDataSizeChanged(0)
        notifyItemRangeChanged(data.startIndex, totalCount - data.startIndex)
    }

    override fun setData(index: Int, data: T) {
        if (index >= this.data.size) {
            return
        }

        val preCount = totalCount
        this.data[index] = data
        notifyDataChange()
        if (totalCount == preCount) {
            notifyItemRangeChanged(data.startIndex + headerLayoutCount, data.getTotalCount())
        } else {
            notifyDataSetChanged()
        }
    }

    override fun setDiffNewData(diffResult: DiffUtil.DiffResult, list: List<T>) {
        if (hasEmptyView()) {
            // If the current view is an empty view, set the new data directly without diff
            setNewInstance(list)
            return
        }
        diffResult.dispatchUpdatesTo(BrvahListUpdateCallback(this))
        this.data = list.toMutableList()
        notifyDataChange()
    }

    override fun setDiffNewData(list: List<T>?, commitCallback: Runnable?) {
        if (hasEmptyView()) {
            // If the current view is an empty view, set the new data directly without diff
            setNewInstance(list)
            commitCallback?.run()
            return
        }

        mDiffHelper?.submitList(list, commitCallback)
        notifyDataChange()
    }

    override fun setNewInstance(list: List<T>?) {
        if (list === this.data) {
            return
        }

        this.data = list?.toMutableList() ?: arrayListOf()
        notifyDataChange()
        mLoadMoreModule?.reset()
        mLastPosition = -1
        notifyDataSetChanged()
        mLoadMoreModule?.checkDisableLoadMoreIfNotFullPage()
    }

    override fun getDefItemCount(): Int {
        return totalCount
    }

    /**
     * 通过Adapter下标找到对应ItemView的类型
     */
    override fun getDefItemViewType(position: Int): Int {
        val entity = getItem(position)
        return entity.getItemType(position - entity.startIndex)
    }

    /**
     * 通过Adapter下标找到对应的数据
     */
    override fun getItem(position: Int): T {
        return getItemOrNull(position)!!
    }

    /**
     * 通过Adapter下标找到对应的数据
     */
    override fun getItemOrNull(position: Int): T? {
        var entity: T? = null
        for (item in data) {
            if (item.isIn(position)) {
                entity = item
            }
        }

        return entity
    }

    /**
     * 如果直接通过[data]改变列表数据，一定得在调用[notifyDataSetChanged]之前调用该方法
     */
    fun notifyDataChange() {
        totalCount = mapData(data)
    }
}